 /*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package edu.marist.cs.os.g3.mos;

import edu.marist.cs.os.g3.processors.CPU;
import edu.marist.cs.os.g3.processors.Channel1;
import edu.marist.cs.os.g3.processors.Channel2;
import edu.marist.cs.os.g3.processors.Channel3;
import edu.marist.cs.os.g3.vm.Drum;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.NoSuchElementException;
import java.util.Queue;
import java.lang.IndexOutOfBoundsException;

/**
 *
 * @author Cameron
 */
public class MOS {

    CPU cpu = new CPU();
    Channel1 channel1;
    Channel2 channel2;
    Channel3 channel3;
    BufferedReader input;
    PrintWriter output;
    PrintWriter trace;
    int cycle = 0;
    //Queues
    Queue<PCB> readyQ = new LinkedList<>();
    Queue<PCB> memoryQ = new LinkedList<>();
    Queue<PCB> ioQ = new LinkedList<>();
    Queue<PCB> terminateQ = new LinkedList<>();
    Queue<PCB> loadQ = new LinkedList<>();
    //Empty buffer queue
    Queue<Buffer> ebq = new LinkedList<>();
    //Inputful buffer queue
    Queue<Buffer> ibq = new LinkedList<>();
    //Outputful buffer queue
    Queue<Buffer> obq = new LinkedList<>();
    Drum drum = new Drum();
    Memory memory = new Memory(cpu);
    ArrayList<PCB> spoolList = new ArrayList<>();
    int processesInSystem = 0;
    //Constants
    final String separator = "===========================================";
    static String someBuffer = " ";
    public static final String PAGE_BLANK = "NONE";

    public MOS(File inputFile, File outputFile, File traceFile) {
        try {
            input = new BufferedReader(new FileReader(inputFile));
            output = new PrintWriter(outputFile);
            trace = new PrintWriter(traceFile);

            channel1 = new Channel1(cpu, input);
            channel2 = new Channel2(cpu, output);
            channel3 = new Channel3(cpu, drum, memory);
            //Create the nine buffers and add them to ebq
            for (int i = 0; i < 9; i++) {
                ebq.add(new Buffer());
            }
            trace.println(separator);
            trace.println("DEFINITIONS AND INITIALIZATIONS");
            trace.println(separator + "\n");
            trace.println("Booting up operating system. Creating CPU, Channel1, Channel2, Channel3.");
            trace.println("Creating ready queue, I/O queue, terminate queue, memory queue, empty buffer queue, input-full buffer queue, output-full buffer queue.");
            trace.println("Creating nine buffers; adding them to empty buffer queue. Channel 1 will \"generate\" the tenth.");
            cpu.IOI = 1;
            trace.println("Setting IOI = 1");

        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
        }
    }

    void executeUserInstruction() {
        if (cycle == 764 || cycle == 765) {
            for (int x = 0; x < 10; x++) {
                trace.println("In funtion executeuserInstution The contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
            }
        }



        cpu.mode = CPU.Mode.SLAVE;
        //trace.println(separator + "\nSLAVE MODE\n" + separator + "\n");
        fetch();
        //If fetch incurred a page fault
        if (cpu.PI == 3) {
            //trace.println("Page fault. Switching to Master Mode.");
            return;
        } else if (cpu.IR.equals("    ")) {
            //found an emtpy instruction
            //trace.println("PCB " + readyQ.element().jobId + " to load queue.");
            contextSwitch(loadQ, readyQ.remove());
            return;
        }
        increment();
        execute();
        if (cycle == 764 || cycle == 765) {
            for (int x = 0; x < 10; x++) {
                trace.println("In function exwcuteuserInstruction The contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
            }
        }

    }

    public void printRegisters() {
        trace.println("MODE: " + cpu.mode.toString() + "  PTR: " + cpu.PTR + "  PI: " + cpu.PI + "  SI: " + cpu.SI + "  TI " + cpu.TI + "  IOI: " + cpu.IOI + "  IR: " + cpu.IR + "  IC: " + String.format("%02d", cpu.IC) + "  R: " + cpu.R + "  C: " + (cpu.C ? "T" : "F"));
    }

    /**
     * Grab the next instruction from memory location in IC and bring to IR.
     */
    void fetch() {
        if (cycle == 764 || cycle == 765) {
            for (int x = 0; x < 10; x++) {
                trace.println("In function fetch The contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
            }
        }

        Block pageTable = memory.getBlock(cpu.PTR);
        // trace.println("Fetching instruction from location " + pageTable.getWord(cpu.IC / 10).word);

        if (pageTable.getWord(cpu.IC / 10).isEmpty()) {
            //We have a page fault.
            trace.append("Page fault encountered in instruction fetch.");
            cpu.PI = 3;
        } else {
            try {
                Word instruction = memory.getWord(cpu.IC, cpu.PTR);
                cpu.PI = 0;
                cpu.IR = instruction.word;
            } catch (NumberFormatException e) {
            }
            //ugly but we'll fix that later. If the cpu fetches an empty, but reserved
            //frame for a program card, PI will be set to 3. This explains the page fault
            //bug.

            //   trace.println("p lines of job " + readyQ.element().jobId + " is" + readyQ.element().programBuffer);
            // trace.println("d lines of job " + readyQ.element().jobId + " is" + readyQ.element().dataBuffer);

            //trace.println("New contents of IR is" + " " + cpu.IR + " of job" + readyQ.element().jobId);
            //trace.println("The length is " + cpu.IR.length());
            if (cycle == 764 || cycle == 765) {
                for (int x = 0; x < 10; x++) {
                    trace.println("In function fetch The contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
                }
            }

        }
    }

    void increment() {
        cpu.IC++;
        //trace.println("The contents of IC" + " " + cpu.IC);

    }

    /**
     * Page faults may occur in all but the Halt instruction.
     * They are always valid for GD and SR, invalid for everything else.
     * TODO: Implement page faults.
     */
    //They are implemented.
    void execute() {
        if (cycle == 764 || cycle == 765) {
            for (int x = 0; x < 10; x++) {
                trace.println("In execute The contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
            }
        }

        int operand = 0;

        if (!cpu.IR.equals("H   ")) {
            try {
                operand = Integer.parseInt(cpu.IR.substring(2, 4));
            } catch (NumberFormatException e) {
                //readyQ.element().terminateReason = PCB.TerminateReason.OPERAND_ERROR;
                //terminateProcess(readyQ.element());

                cpu.PI = 2;
                return;
            }
        }

        int pageTableFrame = cpu.PTR;
        if (memory.getBlock(pageTableFrame).getWord(operand / 10).word.equals("    ")) {
            trace.println("Operand not found in memory.");
            cpu.PI = 3;
        } else {

            switch (cpu.IR.substring(0, 2)) {
                case "LR":
                    String word = memory.getWord(operand, pageTableFrame).toString();
                    if (word != null) {


                        cpu.R = word;
                        // trace.println(" LR instruction executed. R is " + cpu.R);

                    }
                    break;
                case "SR":
                    //trace.println(" SR instruction executed. R is " + cpu.R);
                    memory.getWord(operand, pageTableFrame).empty = true;
                    memory.storeWord(new Word(cpu.R), operand, pageTableFrame);
                    memory.getWord(operand, pageTableFrame).empty = false;
                    //trace.println("CONTENTS ARE " + memory.getWord(operand, pageTableFrame).word);
                    break;
                case "CR":
                    // trace.println("CR executes");

                    //trace.println("R = mem contents? ");
                    /// trace.println("contents in loc " + operand + "= " + memory.getWord(operand, pageTableFrame));
                    if (cpu.R.equals(memory.getWord(operand, pageTableFrame).toString())) {
                        // trace.println("they equate");
                        cpu.C = true;
                    } else {
                        cpu.C = false;
                        // trace.println(" They dont equate");
                    }
                    break;
                case "BT":
                    if (cpu.C) {
                        cpu.IC = operand;
                    }
                    //cpu.C = false;
                    break;
                case "GD":
                    cpu.SI = 1;
                    break;
                case "PD":
                    cpu.SI = 2;
                    break;
                case "H ":
                    cpu.SI = 3;
                    break;
                default:
                    cpu.PI = 1;
            }
        }
        readyQ.element().quantum++;
        readyQ.element().totalTimeCounter++;
        if (readyQ.element().totalTimeCounter == readyQ.element().totalTimeLimit) {
            cpu.TI = 2;
            readyQ.element().TI = 2;
        } else if (readyQ.element().quantum == 10) {
            cpu.TI = 1;
            readyQ.element().TI = 1;
        }

        if (cycle == 764 || cycle == 765) {
            for (int x = 0; x < 10; x++) {
                trace.println("In execute The contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
            } 
        }


    }

    public void run() {
        try {
            if (cycle == 764 || cycle == 765) {
                for (int x = 0; x < 10; x++) {
                    trace.println("In run The contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
                }
            }

            //If there are ofbs, fire Channel 2
            //Fire Channel 3 output spool when PCB on terminate queue
            //Fire Channel 3 input spool when there are input-full buffers
            //Fire Channel 3 GD upon a SI=1, put on IOQ
            //
            //Fire Channel 1 when buffers are empty and not "eof"

            while (!channel1.eof() || !ioQ.isEmpty() || !readyQ.isEmpty() || !terminateQ.isEmpty() || !memoryQ.isEmpty() || !loadQ.isEmpty() || !spoolList.isEmpty()) {
                if (cycle == 6000) {
                    break;
                }

                trace.println("inputful buffer length " + ibq.size());
                trace.println("outoutful buffer length " + obq.size());
                if (ebq.size() >= 4) {
                    trace.println("empty buffer length " + ebq.size());
                }
                trace.println(separator + "\nCYCLE " + cycle + "\n" + separator + "\n");
                if (!readyQ.isEmpty()) {
                    //trace.println(separator + "\nSLAVE MODE\n" + separator);
                    cpu.mode = CPU.Mode.SLAVE;
                    readyQ.element().IC = cpu.IC;
                    executeUserInstruction();

                    try {

                        //if (readyQ.element().jobId.equals("U402")) {
                        trace.println(readyQ.element().jobId + " is active.");
                        trace.println("Displaying program cards" + " " + readyQ.element().programBuffer);
                        trace.println("IR " + cpu.IR);

                        trace.println("IC " + cpu.IC);
                        trace.println(memory);
                        //}

                    } catch (NoSuchElementException e) {
                    }
                    printRegisters();
                }

                /** try {
                for (int k = 0; k < 10; k++) {
                for (int h = 0; h < 10; h++) {
                trace.println("contents of page" + k + "in frame " + memory.getBlock(cpu.PTR).getWord(k).word + "is " + memory.getBlock(Integer.parseInt(memory.getBlock(cpu.PTR).getWord(k).word)).getWord(h).word);
                }
                }
                } catch (NumberFormatException e) {
                }
                 **/
                cpu.mode = CPU.Mode.MASTER;
                channel1.cycleComplete();
                channel2.cycleComplete();
                channel3.cycleComplete();
                trace.println(separator + "\nMASTER MODE\n" + separator);
                interruptHandler();
                taskAssignment();
                cycle++;

                // trace.println("The following blocks are free");
                /** for (int j = 0; j < 100; j++) {
                if (drum.isTrackFree(j)) {
                trace.print("O");
                } else {
                trace.print("X");
                }
                if (j % 20 == 0) {
                trace.println();
                }
                }
                 **/
                try {
                    trace.println("There are " + readyQ.size() + " " + "pcb's on the ready q.");
                    trace.println("There are " + ioQ.size() + " " + "pcb's on the io q.");
                    trace.println("There are " + memoryQ.size() + " " + "pcb's on the memory q. PCB of job " + memoryQ.element().jobId);

                    trace.println("There are " + terminateQ.size() + " " + "pcb's on the terminate q.");
                    trace.println("There are " + loadQ.size() + " " + "pcb's on the load q.");
                } catch (NoSuchElementException e) {
                }
            }
        } finally {
            try {
                input.close();
                output.close();
                trace.close();
            } //Done is a compound condition:
            //We've reached the end of the file, input spooling
            //and there are no more PCBs on any queue
            catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        //Done is a compound condition:
        //We've reached the end of the file, input spooling
        //and there are no more PCBs on any queue
        if (cycle == 764 || cycle == 765) {
            for (int x = 0; x < 10; x++) {
                trace.println("In run The contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
            }
        }

    }

    /**
     * If no active PCB, set CPU registers to values in PCB
     * Take the active PCB, save the CPU's registers to it
     * If there is no other PCB on ready queue, don't do anything
     * If there is another PCB on the ready queue, load its registers to the CPU values
     * Make this PCB the active process
     */
    void contextSwitch(Queue<PCB> queueToGoTo, PCB pcb) {
        if (cycle == 764 || cycle == 765) {
            for (int x = 0; x < 10; x++) {
                trace.println("In contextThe contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
            }
        }

        if (cycle == 399) {
            //trace.println("CONTEXTSWITCHIs frame 2 free? " + memory.getBlock(2).isEmpty());
        }



        if (queueToGoTo == readyQ && readyQ.size() == 0) {
            pcb.quantum = 0;
            pcb.TI = 0;

            readyQ.add(pcb);

            //the head has been removed during the call to this method.
            return;
        }

        if (queueToGoTo == readyQ) {


            pcb.TI = 0;
        } else {
            pcb.TI = cpu.TI;
        }
        if (queueToGoTo == terminateQ) {
            try {
                trace.println("The program " + pcb.jobId + " will terminate and will move to tq");
            } catch (NoSuchElementException e) {
            }
            terminateProcess(pcb);
        } else {
            queueToGoTo.add(pcb);
        }
        //Whether or not the readyQ is empty is already checked in the if statement.
        //I kept those additions commented in case this step is wrong.
        try {
            PCB next = readyQ.element();

            changeRegisters(next);
        } catch (NoSuchElementException e) {
        }
        if (cycle == 399) {
            // trace.println("ENDCONTEXTSWITCHIs frame 2 free? " + memory.getBlock(2).isEmpty());
        }
        if (cycle == 764 || cycle == 765) {
            for (int x = 0; x < 10; x++) {
                trace.println("In context The contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
            }

        }

    }

    void terminateProcess(PCB process) {
        trace.println("IN TERMINATEPROCESSIs frame 2 free? " + memory.getBlock(2).isEmpty());



        //   trace.println("HERE WE END THE PROCESS");
        // trace.println("Process " + process.jobId + " moved to terminate queue.");

        for (int k = 0; k < process.outputLineTracks.size(); k++) {
            trace.println("op data is" + drum.getTrack(process.outputLineTracks.get(k)));
        }
        try {
            int x = process.dataTracks.size() - 1;
            System.out.println("heres x" + x);
            while (x > 0) {
                trace.println("Data on track " + process.dataTracks.get(x) + " is" + " " + drum.getTrack(process.dataTracks.get(x)));
                drum.freeBlock(process.dataTracks.get(x));
                trace.println("Is track number" + process.dataTracks.get(x) + " free?" + " " + drum.getTrack(process.dataTracks.get(x)).isEmpty());

                x--;
            }
            x = process.programTracks.size() - 1;

            while (x > 0) {

                drum.freeBlock(process.programTracks.get(x));
                x--;
            }
            //Eliminate all program card blocks from memory.
            try {
                for (x = 0; x < 10; x++) {
                    memory.freeBlock(Integer.parseInt(memory.getBlock(process.PTR).getWord(x).word));
                }

            } catch (NumberFormatException e) {
            }
            //Eliminate page table
            if (cycle == 764 || cycle == 765) {
                for (int h = 0; x < 10; x++) {
                    trace.println("In terminateprocess The contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
                }
            }
            trace.println("the cycle is" + " " + "and the PTR is " + " " + process.PTR);



            memory.freeBlock(process.PTR);
            if (cycle == 764 || cycle == 765) {
                for (int g = 0; x < 10; x++) {
                    trace.println("In terminateprocess The contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
                    trace.println("THE PROCESS PTR IS " + " " + process.PTR);
                }
            }

        } catch (NoSuchElementException e) {
        }
        // trace.println("Process " + process.jobId + " moved to terminate queue.");
        terminateQ.add(process);
        if (memoryQ.size() > 0) {
            memoryQ.element().PTR = memory.firstFreeBlock();
            memory.getBlock(memoryQ.element().PTR).empty = false;
            if (readyQ.size() == 0) {
                changeRegisters(memoryQ.element());
            }
            readyQ.add(memoryQ.remove());
        }
        if (cycle == 399) {
            //trace.println("ENDTERMINANTEFROCSSIs frame 2 free? " + memory.getBlock(2).isEmpty());
        }
        int i = 0;
        while (i < 100) {
            if (!drum.getTrack(i).isEmpty()) {
                //   trace.println("contents of track " + i + " " + drum.getTrack(i).toString());
            }
            i++;
        }
        if (cycle == 764 || cycle == 765) {
            for (int x = 0; x < 10; x++) {
                trace.println("In terminateprocess The contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
                trace.println("process ptr is" + " " + cpu.PTR);

            }
        }

    }

    /**
     * Contains logic for checking for interrupts and responding accordingly.
     */
    void interruptHandler() {
        if (cycle == 764 || cycle == 765) {
            for (int x = 0; x < 10; x++) {
                trace.println("In interrputHandler The contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
            }
        }

        if (cycle == 399) {
            //  trace.println("Is frame 2 free? " + memory.getBlock(2).isEmpty());
        }
        PCB rq_head = readyQ.peek();
        if (cpu.PI > 0) {
            saveToPCB(rq_head);
            PI();
        }
        if (cpu.SI > 0 && readyQ.contains(rq_head)) {
            saveToPCB(rq_head);
            SI();
        }

        if (cpu.TI > 0 && readyQ.contains(rq_head)) {
            saveToPCB(rq_head);
            TI();
        }


        String bitString = String.format("%3s", Integer.toBinaryString(cpu.IOI)).replace(' ', '0');
        if (bitString.charAt(1) == '1') {
            IR2();
        }
        if (bitString.charAt(0) == '1') {
            IR3();
        }
        if (bitString.charAt(2) == '1') {
            IR1();
        }

        cpu.PI = 0;
        cpu.SI = 0;
        cpu.TI = 0;
        cpu.IOI = 0;
        if (cycle == 399) {
            // trace.println("Is frame 2 free? " + memory.getBlock(2).isEmpty());
        }
        if (cycle == 764 || cycle == 765) {
            for (int x = 0; x < 10; x++) {
                trace.println("In interrputhanlder The contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
            }
        }

    }

    void PI() {
        if (cycle == 764 || cycle == 765) {
            for (int x = 0; x < 10; x++) {
                trace.println("In pi The contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
            }
        }

        if (cycle == 399) {
            //  trace.println("Is frame 2 free? " + memory.getBlock(2).isEmpty());
        }
        PCB rq_head = readyQ.element();

        if (cpu.PI == 1) {
            rq_head.terminateReason = PCB.TerminateReason.OPERATION_CODE_ERROR;
            contextSwitch(terminateQ, readyQ.remove());
        } else if (cpu.PI == 2) {
            rq_head.terminateReason = PCB.TerminateReason.OPERAND_ERROR;
            contextSwitch(terminateQ, readyQ.remove());
        } else if (cpu.PI == 3) {
            //page fault
            Block pageTab = memory.getBlock(cpu.PTR);
            if (!pageTab.getWord((rq_head.IC) / 10).isEmpty()) {
                //Aka if the instruction is in memory but the page of the operand isn't
                if (!(cpu.IR.substring(0, 2).equals("SR") || cpu.IR.substring(0, 2).equals("GD") || cpu.IR.substring(0, 2).equals("BT"))) {
                    // trace.println("Invalid page fault");
                    rq_head.terminateReason = PCB.TerminateReason.INVALID_PAGE_FAULT;
                    contextSwitch(terminateQ, readyQ.remove());
                } else {
                    cpu.IC--;
                    String first_free = new Integer(memory.firstFreeBlock()).toString();
                    if (first_free.equals("-1")) {
                        contextSwitch(memoryQ, readyQ.remove());
                    } else {
                        // trace.println("Allocating frame" + " " + Integer.parseInt(cpu.IR.substring(2, 4)) / 10 + " " + "for operand.");

                        pageTab.getWord(Integer.parseInt(cpu.IR.substring(2, 4)) / 10).setData(first_free);
                        memory.getBlock(Integer.parseInt(first_free)).empty = false;
                        //When we load a single piece of data (not an entire block) into an empty block, we are working with a Word instance.
                        // which DOES set the Word instance.empty = false, but block(x).empty remains true. This
                        //will rectify the issue. This is not a very efficient method  but it will do for now.
                        // pageTab.empty = false;
                    }
                }
            } else {
                //the program card is not in memory
                String first_free = new Integer(memory.firstFreeBlock()).toString();
                if (first_free.equals("-1")) {
                    contextSwitch(memoryQ, readyQ.remove());
                } else {
                    // trace.println("Allocating frame for program card.");
                    Word IC_Page = pageTab.getWord(cpu.IC / 10);
                    IC_Page.setData(first_free);
                    IC_Page.empty = false;
                    memory.getBlock(Integer.parseInt(first_free)).empty = false;
                    //  trace.println("Displaying the content of page table word" + " " + rq_head.IC / 10);
                    // trace.println(pageTab.getWord(rq_head.IC / 10).word);

                }

            }

        }
        if (cycle == 399) {
            //  trace.println("Is frame 2 free? " + memory.getBlock(2).isEmpty());
        }
        if (cycle == 764 || cycle == 765) {
            for (int x = 0; x < 10; x++) {
                trace.println("In pi The contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
            }
        }

    }

    void SI() {
        if (cycle == 764 || cycle == 765) {
            for (int x = 0; x < 10; x++) {
                trace.println("In SI The contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
            }
        }

        if (cycle == 399) {
            // trace.println("Is frame 2 free? " + memory.getBlock(2).isEmpty());
        }
        PCB rq_head = readyQ.element();
        if (cpu.SI != 3) {
            contextSwitch(ioQ, readyQ.remove());
        } else {
            trace.println("The application will terminate");
            rq_head.terminateReason = PCB.TerminateReason.NORMAL;

            contextSwitch(terminateQ, readyQ.remove());
        }
        if (cycle == 399) {
            // trace.println("Is frame 2 free? " + memory.getBlock(2).isEmpty());
        }
        if (cycle == 764 || cycle == 765) {
            for (int x = 0; x < 10; x++) {
                trace.println("iN SI PThe contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
            }
        }

    }

    void TI() {
        if (cycle == 399) {
            //trace.println("Is frame 2 free? " + memory.getBlock(2).isEmpty());
        }
        if (cycle == 764 || cycle == 765) {
            for (int x = 0; x < 10; x++) {
                trace.println("iiN TI The contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
            }
        }

        PCB rq_head = readyQ.element();

        if (cpu.TI == 1) {
            rq_head.quantum = 0;
            contextSwitch(readyQ, readyQ.remove());
        } else {

            rq_head.terminateReason = PCB.TerminateReason.TIME_LIMIT_EXCEEDED;
            contextSwitch(terminateQ, readyQ.remove());
        }
        if (cycle == 399) {
            // trace.println("Is frame 2 free? " + memory.getBlock(2).isEmpty());
        }
        if (cycle == 764 || cycle == 765) {
            for (int x = 0; x < 10; x++) {
                trace.println("IN TI The contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
            }
        }

    }

    /**
     * The interrupt routine for Channel 1 completion
     */
    void IR1() {
        if (cycle == 764 || cycle == 765) {
            for (int x = 0; x < 10; x++) {
                trace.println("IN IR1 The contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
            }
        }

        Buffer ifb = channel1.getBuffer();
        ifb.read = false;
        ifb.spoolOrder = 0;

        if (!spoolList.isEmpty()) {
            if (spoolList.get(0).jobId.equals("G701")) {
                someBuffer = someBuffer + ifb.toString();
            }
        }
        ibq.add(ifb);
        trace.println("Adding input-full buffer to input-full buffer queue.");
        trace.println("Size of input-full buffer queue: " + ibq.size());
        //if (!channel1.eof() && !channel1.isBusy() && !ebq.isEmpty()) {
        //    channel1.startTask(ebq.remove());
        //}
        try {
            trace.println("The contents of the head of the input queeu" + " " + ibq.element());
        } catch (NoSuchElementException e) {
        }

        //Examine ifb
        ifb = ibq.remove();


        trace.println("Removing buffer from input-full buffer queue. Contents: " + ifb.toString());

        String card = ifb.toString();
        if (card.length() < 4) {
            //pad to 40
            for (int i = 0; i < 40 - card.length(); i++) {
                card += " ";
            }
        }
        switch (card.substring(0, 4)) {
            case "$AMJ":
                trace.println("$AMJ encountered.");
                PCB spooling = new PCB();
                trace.println("Creating PCB.");
                spooling.jobId = card.substring(4, 8);
                spooling.totalTimeLimit = Integer.parseInt(card.substring(8, 12));
                spooling.totalLineLimit = Integer.parseInt(card.substring(12, 16));
                spooling.followingCards = PCB.FollowingCards.PROGRAM;
                spooling.jobNumber = ++processesInSystem;

                //Allocate frame for page table will take place when spooling has finished
                //A process is not ready until it has a value in its PTR field.
                spoolList.add(spooling);
                ifb.setEmpty();
                ebq.add(ifb);
                break;
            case "$DTA":

                int index = 0;
                spooling = spoolList.get(index);

                if (spooling.jobId.equals("G701")) {
                    trace.println("Cards read thus far " + someBuffer);

                }
                //A process can have seen the $END card without writing all of the
                //cards to the drum. Thus, we need to keep track of the next process
                //that these cards are for.
                //"Done input spooling" is when the last card has been written to the drum.
                while (spooling.followingCards == PCB.FollowingCards.NONE) {
                    spooling = spoolList.get(++index);
                    trace.println("hellp");

                }
                spooling.followingCards = PCB.FollowingCards.DATA;
                trace.println("Program tracks size is " + " " + spooling.programTracks.size());

                //  trace.println("DTA HAS BEEN READ");
                //trace.println(drum.getTrack(spooling.dataTracks.get(0)).toString());

                ifb.setEmpty();
                ebq.add(ifb);
                Buffer.order = 0;
                break;
            case "$EOJ":
                index = 0;
                spooling = spoolList.get(index);
                // trace.println("EOJ FOUND" + spooling.jobId);

                while (spooling.followingCards == PCB.FollowingCards.NONE) {
                    spooling = spoolList.get(++index);
                    trace.println("hellp");

                }
                spooling.followingCards = PCB.FollowingCards.NONE;
                trace.println("following " + spooling.followingCards + spooling.jobId);
                ifb.setEmpty();
                ebq.add(ifb);
                Buffer.order = 0;
                //spooling.dataTally = (spooling.numOfDataCards + spooling.numOfProgramCards) - (spooling.programTracks.size() + spooling.dataTracks.size());
                break;
            default:

                index = 0;
                spooling = spoolList.get(index);
                if (spooling.jobId.equals("U401")) {
                    trace.println("Displaying card" + " " + ifb.toString());
                }
                while (spooling.followingCards == PCB.FollowingCards.NONE) {
                    trace.println("hellp");
                    if (index + 1 < spoolList.size()) {
                        spooling = spoolList.get(++index);

                    }
                }
                if (cycle >= 910) {
                    trace.println("ibq.read?" + ifb.read);


                }
                if (!ifb.read) {
                    switch (spooling.followingCards) {
                        case PROGRAM:
                            trace.println("JOB ID OF THE PCB AT THE HEAD IS" + spooling.jobId);
                            ifb.cardType = PCB.FollowingCards.PROGRAM;

                            spooling.numOfProgramCards++;

                            ifb.jID = spooling.jobId;
                            ifb.read = true;
                            ifb.spoolOrder = Buffer.order;
                            Buffer.order++;
                            spooling.programTracks.add(-1);
                            break;
                        case DATA:

                            ifb.cardType = PCB.FollowingCards.DATA;
                            spooling.numOfDataCards++;
                            ifb.jID = spooling.jobId;
                            ifb.read = true;
                            ifb.spoolOrder = Buffer.order;
                            Buffer.order++;
                            spooling.dataTracks.add(-1);
                            break;
                    }

                }
                if (cycle >= 910) {
                    trace.println("ibq.read?" + ifb.read);


                }
                ibq.add(ifb);

                trace.println("Displaying the inputfull buffer" + " " + ibq.toString());

        }
        if (cycle == 399) {
            trace.println("Is frame 2 free? " + memory.getBlock(2).isEmpty());
        }
        if (cycle == 764 || cycle == 765) {
            for (int x = 0; x < 10; x++) {
                trace.println("In IR1AThe contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
            }
        }

    }

    /**
     * The interrupt routine for Channel 2 completion
     */
    void IR2() {
        if (cycle == 399) {
            // trace.println("Is frame 2 free? " + memory.getBlock(2).isEmpty());
        }
        if (cycle == 764 || cycle == 765) {
            for (int x = 0; x < 10; x++) {
                trace.println("IIN IR2 The contents of ,PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
            }
        }

        Buffer efb = channel2.getBuffer();
        ebq.add(efb);
        if (cycle == 399) {
            // trace.println("Is frame 2 free? " + memory.getBlock(2).isEmpty());
        }
        if (cycle == 764 || cycle == 765) {
            for (int x = 0; x < 10; x++) {
                trace.println("The contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
            }
        }

    }
    PCB.FollowingCards lastCardType = PCB.FollowingCards.NONE;

    /**
     * The interrupt routine for Channel 3 completion
     */
    void IR3() {
        if (cycle == 399) {
            //    trace.println("Is frame 2 free? " + memory.getBlock(2).isEmpty());
        }
        if (cycle == 764 || cycle == 765) {
            for (int x = 0; x < 10; x++) {
                trace.println("IN IR3The contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
            }
        }

        switch (channel3.getTask()) {
            case INPUT_SPOOL:
                //Buffer has been set empty in Channel 3





                Buffer eb = channel3.getBuffer();
                trace.println("The new arrived line is" + " " + eb.toString());

                trace.println("The head of the spooling queue is" + " " + spoolList.get(0).jobId);
                int theOrder = 0;

                while (theOrder < eb.spoolOrder) {
                    theOrder++;
                }
                eb.setEmpty();
                ebq.add(eb);
                int trackNum = channel3.getTrack();
                int index = 0;
                while (!spoolList.get(index).jobId.equals(eb.jID)) {
                    index++;
                }


                /* int trackNum = channel3.getTrack();
                PCB spooling = spoolList.get(0);
                if (spooling.numOfProgramCards > 0) {
                spooling.programTracks.add(trackNum);
                spooling.numOfProgramCards--;
                } else if (spooling.numOfDataCards > 0) {
                spooling.dataTracks.add(trackNum);
                spooling.numOfDataCards--;
                
                }**/
                trace.println("The jid of teh buffer is " + " " + eb.jID);
                trace.println("The jid of the pcb is" + " " + spoolList.get(index).jobId);
                if (eb.cardType == PCB.FollowingCards.DATA) {
                    spoolList.get(index).dataBuffer = spoolList.get(index).dataBuffer + " " + eb.toString();

                } else {
                    spoolList.get(index).programBuffer = spoolList.get(index).programBuffer + " " + eb.toString();
                }
                trace.println("The program tracks are " + spoolList.get(index).programBuffer);
                trace.println("The data tracks are " + spoolList.get(index).dataBuffer);

                PCB spooling = spoolList.get(index);

                switch (eb.cardType) {
                    case PROGRAM:
                        spooling.programTracks.set(theOrder, trackNum);
                        break;
                    case DATA:

                        spooling.dataTracks.set(theOrder, trackNum);
                        break;
                }


                /**  if (spoolList.get(0).dataTracks.size() == spoolList.get(0).numOfDataCards && spoolList.get(0).followingCards == PCB.FollowingCards.NONE && spoolList.get(index).programTracks.size() == spoolList.get(index).numOfProgramCards) {
                spoolList.get(index).finishedInputSpooling = true;
                
                }**/
                trace.println("Number of program tracks in PCB: " + spooling.programTracks.size() + " of program " + spooling.jobId);
                trace.println("Number of data tracks in PCB: " + spooling.dataTracks.size());
                trace.println("nofPorgram cards: " + spooling.numOfProgramCards + " of program " + spooling.jobId);
                trace.println("nofData cards: " + spooling.numOfDataCards + " of program " + spooling.jobId);
                trace.println(spooling.finishedInputSpooling);

                break;

            case OUTPUT_SPOOL:

                PCB tq_head = terminateQ.element();
                Buffer ofb = channel3.getBuffer();
                obq.add(ofb);
                tq_head.outputLineTracks.remove(0);


                break;

            case LOAD:
                // trace.println("In IR3() case LOAD" + " " + memory.getBlock(cpu.PTR).getWord(cpu.IC / 10).word + " instruction card" + " " + drum.getTrack(channel3.getTrack()).toString());

                PCB l_head = loadQ.element();
                drum.freeBlock(l_head.programTracks.get(0));

                int k = l_head.programTracks.remove(0);
                trace.println("First program track of the process" + " " + k);

                //l_head.numOfProgramCards--;
                if (l_head.TI == 2) {
                    l_head.terminateReason = PCB.TerminateReason.TIME_LIMIT_EXCEEDED;
                    terminateProcess(loadQ.remove());
                } else {
                    if (readyQ.isEmpty()) {
                        changeRegisters(l_head);
                    }
                    readyQ.add(loadQ.remove());

                }
                if (loadQ.size() > 0) {
                    trace.println("First program trak of the ither proess" + " " + loadQ.element().programTracks.get(0));

                }
                break;
            case GD:

                PCB GD_head = ioQ.element();
                drum.freeBlock(GD_head.dataTracks.get(0));
                GD_head.dataTracks.remove(0);

                if (GD_head.TI == 2) {
                    GD_head.terminateReason = PCB.TerminateReason.TIME_LIMIT_EXCEEDED;
                    terminateProcess(ioQ.remove());
                } else {
                    if (readyQ.isEmpty()) {
                        changeRegisters(GD_head);
                    }
                    readyQ.add(ioQ.remove());

                }
                break;
            case PD:
                PCB PD_head = ioQ.peek();
                PD_head.outputLineTracks.add(new Integer(channel3.getTrack()));
                PD_head.lineLimitCounter++;
                if (PD_head.TI == 2) {

                    PD_head.terminateReason = PCB.TerminateReason.TIME_LIMIT_EXCEEDED;
                    terminateProcess(ioQ.remove());
                } else {
                    if (readyQ.isEmpty()) {
                        changeRegisters(PD_head);
                    }
                    readyQ.add(ioQ.remove());


                }
                break;

        }
        if (cycle == 399) {
            // trace.println("Is frame 2 free? " + memory.getBlock(2).isEmpty());
        }
        if (cycle == 764 || cycle == 765) {
            for (int x = 0; x < 10; x++) {
                trace.println("IN IR3The contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
            }
        }

    }

    /**
     * Channel tasks are assigned in this method
     */
    void taskAssignment() {
        if (cycle == 399) {
            //trace.println("Is frame 2 free? " + memory.getBlock(2).isEmpty());
        }
        if (cycle == 764 || cycle == 765) {
            for (int x = 0; x < 10; x++) {
                trace.println("IN TASKASSIGNMENTThe contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
            }
        }

        trace.println(separator + "\nTASK ASSIGNMENT\n" + separator + "\n");
        assignChannel1();
        assignChannel2();
        assignChannel3();
        if (cycle == 399) {
            // trace.println("Is frame 2 free? " + memory.getBlock(2).isEmpty());
        }
        if (cycle == 764 || cycle == 765) {
            for (int x = 0; x < 10; x++) {
                trace.println("IN TASKASSIGNMENTThe contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
            }
        }

    }

    /* //For Channel 3, when finishedInputSpooling is false.
    PCB getActivelyInputSpooilingProcess() {
    int index = 0;
    PCB spooling = spoolList.get(index);
    while (spooling.finishedInputSpooling) {
    spooling = spoolList.get(++index);
    }
    return spooling;
    }*/
    private void assignChannel1() {
        if (cycle == 764 || cycle == 765) {
            for (int x = 0; x < 10; x++) {
                trace.println("The contents of PTR 9 WORD" + " " + x + " " + "are" + " " + memory.getBlock(9).getWord(x));
            }
        }

        if (cycle == 399) {
            trace.println("Is frame 2 free? " + memory.getBlock(2).isEmpty());
        }
        trace.println("Empty buffer queue size: " + ebq.size());
        trace.println("Channel 1 busy: " + channel1.isBusy());
        trace.println("Cards left to read: " + !channel1.eof());

        if (!channel1.isBusy()) {
            if ((!channel1.eof()) && (!ebq.isEmpty())) {
                channel1.startTask(ebq.remove());
                trace.println("Assigning task to Channel 1; using an empty buffer.");

            } else if ((channel1.eof()) && (spoolList.get(0).followingCards != PCB.FollowingCards.NONE )) {
                channel1.startTask();
                trace.println("Assigning task to Channel 1.");

            } else {
                trace.println("Not assigning task to Channel 1.");
            }
        }
    }

    private void assignChannel2() {
        if (cycle == 399) {
            // trace.println("Is frame 2 free? " + memory.getBlock(2).isEmpty());
        }
        trace.println("Output-full buffer queue size: " + obq.size());
        trace.println("Channel 2 busy: " + channel2.isBusy());
        if (!obq.isEmpty() && !channel2.isBusy()) {
            channel2.startTask(obq.remove());
            trace.println("Assigning task to Channel 2; using an output-full buffer.");
        }

        trace.println("Not assigning task to Channel 2.");
        if (cycle == 399) {
            //trace.println("Is frame 2 free? " + memory.getBlock(2).isEmpty());
        }
    }

    private void assignChannel3() {
//For each case: We don't need to check for empty frames since the process
//already has a frame referenced in the page table by the time it reaches here.
        if (cycle == 399) {
            //trace.println("Is frame 2 free? " + memory.getBlock(2).isEmpty());
        }
        //OS is prioriy 1
        if (!channel3.isBusy() && !terminateQ.isEmpty() && !ebq.isEmpty()) {
            if (!terminateQ.element().startedOutputSpooling) {
                if (ebq.size() >= 4) {
                    String reasonText = "";
                    switch (terminateQ.element().terminateReason) {
                        case NORMAL:
                            reasonText = "Normal Termination";
                            break;
                        case OUT_OF_DATA:
                            reasonText = "Out of Data";
                            break;
                        case LINE_LIMIT_EXCEEDED:
                            reasonText = "Line Limit Exceeded";
                            break;
                        case TIME_LIMIT_EXCEEDED:
                            reasonText = "Time Limit Exceeded";
                            break;
                        case OPERATION_CODE_ERROR:
                            reasonText = "Operation Code Error";
                            break;
                        case OPERAND_ERROR:
                            reasonText = "Operand Error";
                            break;
                        case INVALID_PAGE_FAULT:
                            reasonText = "Invalid Page Fault";
                            break;
                    }





                    trace.println("4 empty buffers are available; generating lines of messages and two blank lines.");
                    //Prepare output messages
                    Buffer firstLine = ebq.remove();
                    Buffer secondLine = ebq.remove();
                    Buffer firstBlank = ebq.remove();
                    Buffer secondBlank = ebq.remove();

                    firstBlank.setData(" ");
                    secondBlank.setData(" ");

                    firstLine.setData(terminateQ.element().jobId + "   " + reasonText);
                    secondLine.setData(String.format("%04d", terminateQ.element().IC) + "   " + terminateQ.element().IR + "   " + terminateQ.element().R + "   " + (terminateQ.element().C ? "T" : "F") + "   " + terminateQ.element().totalTimeCounter + "   " + terminateQ.element().lineLimitCounter);

                    //Clear every data track of the PCB's process


                    //Now add everything to the output-full buffer queue
                    obq.add(firstLine);
                    obq.add(secondLine);
                    obq.add(firstBlank);
                    obq.add(secondBlank);

                    //Output spooling has begun, since these messages were generated
                    //If these messages cannot be generated now, we have to delay output
                    //spooling until they can be.


                    terminateQ.element().startedOutputSpooling = true;
                } else {
                    trace.println("4 empty buffers not available, must wait until available before spooling.");
                }


            } else if (terminateQ.element().outputLineTracks.isEmpty()) {

                trace.println("done with spooling output line trscks. Time to (try to)output spool two blanks.");
                if (ebq.size() < 2) {
                    trace.println("Not enough buffers on ebq. Two buffers are needed to send two blank lines to channel 2 for output. PCB will remain on the terminateQ.");
                } else {
                    trace.println("Sending two ofb's filled with blanks to ofbq for output.");
                    Buffer blank1 = ebq.remove();
                    Buffer blank2 = ebq.remove();
                    String blank = " ";
                    for (int x = 0; x < 39; x++) {
                        blank = blank + " ";
                    }
                    blank1.setData(blank);
                    blank2.setData(blank);
                    obq.add(blank1);
                    obq.add(blank2);
                    //bye pcb
                    terminateQ.remove();
                }
            } else {

                channel3.startTask(Channel3.Task.OUTPUT_SPOOL, ebq.remove(), terminateQ.element().outputLineTracks.get(0), -1);
            }
        }
        //GD or PD
        if (!channel3.isBusy() && !ioQ.isEmpty()) {
            PCB process = ioQ.element();
            //Make GD the priority

            if (process.SI == 1) {
                // trace.println("The PROESS IR is " + process.IR);
                //If no more data tracks, out of data termination
                if (process.dataTracks.isEmpty()) {
                    trace.println("out of data.proess will terminate");
                    process.terminateReason = PCB.TerminateReason.OUT_OF_DATA;
                    terminateProcess(ioQ.remove());
                } else {
                    //Translate given address
                    try {
                        int virtualAddress = Integer.parseInt(process.IR.substring(2, 4));
                        int frame = Integer.parseInt(memory.getBlock(process.PTR).getWord(virtualAddress / 10).word);
                        //channel3.startTask(Channel3.Task.GD, null, process.dataTracks.get(0), memory.firstFreeBlock());
                        if (process.jobId.equals("U301")) {
                            // trace.println("Ready to access data " + drum.getTrack(process.dataTracks.get(0)).toString());
                            // trace.println(process.dataTracks.size());
                        }
                        channel3.startTask(Channel3.Task.GD, null, process.dataTracks.get(0), frame);
                    } catch (NumberFormatException e) {
                    }
                }
            } else if (process.SI == 2) {
                if (process.lineLimitCounter > process.totalLineLimit) {
                    trace.println("Invalid PD attempt. lineLimitCounter == totalLineLimit. The process will terminate. ");
                    trace.println("The counter is " + process.lineLimitCounter);
                    trace.println("The limit is " + process.totalLineLimit);
                    trace.println("The process number is " + process.jobNumber);

                    process.terminateReason = PCB.TerminateReason.LINE_LIMIT_EXCEEDED;
                    contextSwitch(terminateQ, ioQ.remove());
                } else if (drum.firstFreeTrack() != -1) //Address translation of operand goes here
                {
                    try {
                        int virtualAddress = Integer.parseInt(process.IR.substring(2, 4));
                        int frame = Integer.parseInt(memory.getBlock(process.PTR).getWord(virtualAddress / 10).word);
                        //channel3.startTask(Channel3.Task.PD, null, drum.firstFreeTrack(), process.outputLineTracks.get(0));
                        channel3.startTask(Channel3.Task.PD, null, drum.firstFreeTrack(), frame);
                    } catch (NumberFormatException e) {
                    }


                }
            } else if (process.SI == 3) {
                process.terminateReason = PCB.TerminateReason.NORMAL;
                contextSwitch(terminateQ, ioQ.remove());
            }
        }


        //Load
        //Warning: we shouldn't have to worry about whether or not there's room in memory
        //since there is already an allocated frame for the page.
        if (!channel3.isBusy() && !loadQ.isEmpty()) {
            try {
                PCB process = loadQ.element();


                for (int x = 0; x < process.programTracks.size(); x++) {
                    trace.println("Displaying program tracks" + drum.getTrack(process.programTracks.get(x)));
                }
                int track = process.programTracks.get(0);
                try {
                    Integer frame = Integer.parseInt(memory.getBlock(process.PTR).getWord(process.IC / 10).word);
                    channel3.startTask(Channel3.Task.LOAD, null, track, frame);

                } catch (NumberFormatException e) {
                }
            } catch (IndexOutOfBoundsException e) {
                trace.println("UH OH OUT OF DATA");
            }
        }
        //        throw new UnsupportedOperationException("Not yet implemented");
        //Input Spooling
        trace.println("Size of input-full buffer queue: " + ibq.size());
        trace.println("Channel 3 busy: " + channel3.isBusy());
        if (spoolList.size() > 0) {
            PCB spooling = spoolList.get(0);


            /** for (int x = 0; x < spooling.dataTracks.size(); x++) {
            
            trace.println("Displaying dataTrack contents " + drum.getTrack(spooling.dataTracks.get(x)).toString());
            
            }**/
            if (spooling.followingCards == PCB.FollowingCards.NONE && !spooling.programTracks.contains(-1) && !spooling.dataTracks.contains(-1)) {
                //Finished input spooling
                trace.println("followingcards of pcb of program " + spooling.jobId + " is " + spooling.followingCards);
                trace.println("numofDataCards of pcb of program " + spooling.jobId + " is " + spooling.numOfDataCards);
                trace.println("dataTracks.size() of pcb of program " + spooling.jobId + " is " + spooling.dataTracks.size());

                /**  for (int x = 0; x < spooling.programTracks.size(); x++) {
                spooling.programBuffer = spooling.programBuffer + " " + drum.getTrack(spooling.programTracks.get(x)).toString();
                
                }**/
                //trace.println("Displaying program tracks of program" + " " + spooling.jobId + " " + spooling.programBuffer);
                /** for (int x = 0; x < spooling.dataTracks.size(); x++) {
                spooling.dataBuffer = spooling.dataBuffer + " " + drum.getTrack(spooling.dataTracks.get(x)).toString();
                }**/
                // trace.println("Displaying data tracks of program" + " " + spooling.jobId + " " + spooling.dataBuffer);
                trace.println("Finished input spooling PCB " + spooling.jobId);
                //spooling.finishedInputSpooling = true;
                //Only two living processes at a time.
                if (readyQ.size() + loadQ.size() + ioQ.size() <= 1) {
                    //Allocate page table

                    //Move to ready queue
                    if (allocatePageTable(spooling)) {
                        if (readyQ.isEmpty()) {
                            //the newly spooled pcb will be at the front of the queue. We need to reload the registers with
                            //the appropriate values.
                            changeRegisters(spooling);




                        }

                        readyQ.add(spoolList.remove(0));

                    } else {
                        trace.println("To memoryq");
                        memoryQ.add(spoolList.remove(0));
                    }
                } else {
                    trace.println("To memoryq");
                    memoryQ.add(spoolList.remove(0));
                }
            }
        }
        if (!spoolList.isEmpty()) {
            try {

                if (spoolList.get(0).jobId.equals("U902")) {
                    if (!ibq.isEmpty()) {
                        trace.println("it is not empty");

                    } else {
                        trace.println("it is, empty");
                    }
                    if (!channel3.isBusy()) {
                        trace.println("it is not busy");
                    } else {
                        trace.println("it is busy");
                    }
                    if (!ibq.element().read) {
                        trace.println("element not read");
                    } else {
                        trace.println("element has been read");

                    }
                }
            } catch (NoSuchElementException e) {
            }
        }
        if (!ibq.isEmpty() && !channel3.isBusy()) {
            //If a free track
            if (ibq.element().read) {
                ibq.element().read = false;
                trace.println("Drum track available: " + drum.firstFreeTrack());
                trace.println("IN CHAN 3 ASSIGN the contents of the ibq top" + " " + ibq.element().toString());

                if (drum.firstFreeTrack() != -1) {
                    channel3.startTask(Channel3.Task.INPUT_SPOOL, ibq.remove(), drum.firstFreeTrack(), -1);
                }
            }
            //trace.println("In TaskAssign" + " " + cpu.PTR);
            if (cycle == 399) {
                // trace.println("Is frame 2 free? " + memory.getBlock(2).isEmpty());
            }

        }
    }

    void saveToPCB(PCB pcb) {
        pcb.C = cpu.C;
        pcb.IC = cpu.IC;
        pcb.IR = cpu.IR;
        pcb.PTR = cpu.PTR;
        pcb.R = cpu.R;
        pcb.SI = cpu.SI;
        pcb.PI = 0;
        pcb.quantum = 0;



    }

    void changeRegisters(PCB onDeck) {
        if (cycle == 399) {
            // trace.println("Is frame 2 free? " + memory.getBlock(2).isEmpty());
        }
        cpu.PTR = onDeck.PTR;
        cpu.C = onDeck.C;
        cpu.IC = onDeck.IC;
        cpu.PI = 0;
        cpu.TI = 0;
        cpu.SI = 0;
        cpu.R = onDeck.R;
        if (cycle == 399) {
            //trace.println("Is frame 2 free? " + memory.getBlock(2).isEmpty());
        }

    }

    private boolean allocatePageTable(PCB spooling) {
        if (cycle == 399) {
            //trace.println("Is frame 2 free? " + memory.getBlock(2).isEmpty());
        }
        //Find a free fram
        //If no free frame, return false
        //Else put frame number in PCB's PTR value
        //Return true
        spooling.PTR = memory.firstFreeBlock();
        if (spooling.PTR >= 0) {
            memory.getBlock(spooling.PTR).empty = false;
        }
        return (spooling.PTR != -1);
    }
}
